﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
using System.DirectoryServices.Protocols;
using System.Text;
using ActiveDirectoryUtilities.CommonLibrary;
using ActiveDirectoryUtilities.DirectoryServiceAccess;
using ActiveDirectoryUtilities.ReplDiag;
using Microsoft.SqlServer.Server;
using AD = System.DirectoryServices.ActiveDirectory;
using System.Security.Principal;
using System.Diagnostics;
using AccessActiveDirectory;

public partial class UserDefinedFunctions
{

	[SqlFunction(FillRowMethodName = "FillReplDiagRow", DataAccess = DataAccessKind.Read
			, TableDefinition =
			"[" + ProcessReplicationInformation.COLUMN_NAME_FAIL_TYPE + "] NVARCHAR(MAX)"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_IS_CRITICAL + "] BIT"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_DESTINATION_DC + "] NVARCHAR(MAX)"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_SITE_NAME + "] NVARCHAR(MAX)"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_NAMING_CONTEXT + "] NVARCHAR(MAX)"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_IS_WRITABLE + "] BIT"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_SOURCE_DC + "] NVARCHAR(MAX)"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_LAST_SUCCESSFUL_SYNC + "] DATETIME"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_NUMBER_OF_SYNC_FAILURE_ATTEMPTS + "] INT"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_ERROR_CODE + "] INT"
			+ ", [" + ProcessReplicationInformation.COLUMN_NAME_ERROR_MESSAGE + "] NVARCHAR(MAX)"
			)]
	public static IEnumerable fn_GetReplicationStatus(SqlString forestFqdn, SqlBoolean useRobustDcLocation)
	{
		EventLogTraceListener eltl = new EventLogTraceListener("Application");
		if (ConfiguredSettings.TraceLevel >= 4)
		{
			Trace.Listeners.Add(eltl);
		}

		WindowsImpersonationContext executionContext = null;
		try
		{
			WindowsIdentity callerId = SqlContext.WindowsIdentity;
			if (callerId != null)
			{
				executionContext = callerId.Impersonate();

				string internalForestFqdn = string.Empty;
				if (!forestFqdn.IsNull)
				{
					internalForestFqdn = (string)forestFqdn;
				}

				bool useRDL;
				if (useRobustDcLocation.IsNull)
				{
					useRDL = false;
				}
				else
				{
					useRDL = (bool)useRobustDcLocation;
				}

				List<DomainControllerReplicationInfo> _dcs = new List<DomainControllerReplicationInfo>();

				WorkItemQueue wiq = new WorkItemQueue(100);
				Forest f = null;
				try
				{
					if (string.IsNullOrEmpty(internalForestFqdn))
					{
						f = Forest.GetCurrentForest();
					}
					else
					{
						f = Forest.GetForest(internalForestFqdn);
					}
				}
				catch (LdapException ex)
				{
					if (ex.ErrorCode == 81)
					{
						throw new ApplicationException("Couldn't contact a DC for the forest root.  Ensure DNS 'A' records for the forest root domain are properly registered.");
					}
					else
					{
						SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
						throw ex;
					}
				}
				catch (Exception ex)
				{
					SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
					throw ex;
				}

				foreach (Domain d in f.Domains)
				{
					try
					{
						List<DomainController> dcs = d.FindAllDomainControllers(useRDL);
						foreach (DomainController dc in dcs)
						{
							if (dc.IsDsEnabled)
							{
								DomainControllerReplicationInfo dcri = new DomainControllerReplicationInfo(dc.DnsHostName);
								dcri.NtDsaGuid = dc.NtdsDsaObjectGuid;
								dcri.SiteName = dc.SiteName.ToLower();
								dcri.NtdsDsaObjectName = dc.NtdsDsaObjectName;
								_dcs.Add(dcri);
								wiq.EnqueueWorkItem(ProcessReplicationInformation.CollectDCInformation, dcri);
							}
							else
							{
								DomainControllerReplicationInfo dcri = new DomainControllerReplicationInfo(dc.DnsHostName);
								dcri.ErrorMessage = "Directory Services are not running on" + dc.Name;
								_dcs.Add(dcri);
							}
						}
					}
					catch (DirectoryServiceAccessException)
					{
						throw new ApplicationException("Could not contact domain:  " + d.DnsDomainName);
					}
				}
				wiq.WaitAllWorkItems();

				ProcessReplicationInformation pri = new ProcessReplicationInformation(_dcs);
				return pri.ReplicationTopologyStateData.Rows;
			}
		}
		finally
		{
			if (Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Remove(eltl);
			}
			if (executionContext != null)
			{
				executionContext.Undo();
			}
		}
		return null;

	}

	private static void FillReplDiagRow(
		object o
		, out SqlString failType
		, out SqlBoolean isCritical
		, out SqlString destinationDC
		, out SqlString siteName
		, out SqlString namingContext
		, out SqlBoolean isWritable
		, out SqlString sourceDC
		, out SqlDateTime lastSuccessfulSync
		, out SqlInt32 numSyncFailures
		, out SqlInt32 errorCode
		, out SqlString errorMessage
		)
	{
		DataRow dr = (DataRow)o;
		failType = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_FAIL_TYPE]);
		isCritical = (SqlBoolean)((bool)dr[ProcessReplicationInformation.COLUMN_NAME_IS_CRITICAL]);
		destinationDC = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_DESTINATION_DC]);
		siteName = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_SITE_NAME]);
		namingContext = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_NAMING_CONTEXT]);
		sourceDC = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_SOURCE_DC]);
		errorMessage = (SqlString)((string)dr[ProcessReplicationInformation.COLUMN_NAME_ERROR_MESSAGE]);

		if (dr[ProcessReplicationInformation.COLUMN_NAME_IS_WRITABLE] != DBNull.Value)
		{
			try
			{
				isWritable = (SqlBoolean)((bool)dr[ProcessReplicationInformation.COLUMN_NAME_IS_WRITABLE]);
			}
			catch (InvalidCastException)
			{
				isWritable = SqlBoolean.Null;
			}
		}
		else
		{
			isWritable = SqlBoolean.Null;
		}

		if (dr[ProcessReplicationInformation.COLUMN_NAME_LAST_SUCCESSFUL_SYNC] != DBNull.Value)
		{
			try
			{
				lastSuccessfulSync = (SqlDateTime)((DateTime)dr[ProcessReplicationInformation.COLUMN_NAME_LAST_SUCCESSFUL_SYNC]);
			}
			catch (InvalidCastException)
			{
				lastSuccessfulSync = SqlDateTime.Null;
			}
		}
		else
		{
			lastSuccessfulSync = SqlDateTime.Null;
		}

		if (dr[ProcessReplicationInformation.COLUMN_NAME_NUMBER_OF_SYNC_FAILURE_ATTEMPTS] != DBNull.Value)
		{
			try
			{
				numSyncFailures = (SqlInt32)((int)dr[ProcessReplicationInformation.COLUMN_NAME_NUMBER_OF_SYNC_FAILURE_ATTEMPTS]);
			}
			catch (InvalidCastException)
			{
				numSyncFailures = SqlInt32.Null;
			}
		}
		else
		{
			numSyncFailures = SqlInt32.Null;
		}

		if (dr[ProcessReplicationInformation.COLUMN_NAME_ERROR_CODE] != DBNull.Value)
		{
			try
			{
				errorCode = (SqlInt32)((int)dr[ProcessReplicationInformation.COLUMN_NAME_ERROR_CODE]);
			}
			catch (InvalidCastException)
			{
				errorCode = SqlInt32.Null;
			}
		}
		else
		{
			errorCode = SqlInt32.Null;
		}
	}
};

